package com.jsbb.util;

import com.jsbb.pojo.ApiParam;
import com.jsbb.pojo.ApiTable;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * POI Word工具类
 *
 * @author w
 * @date 2021-03-05
 */
public class WordUtils {

    private static Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);

    public static void main(String[] args) throws Exception {
        String api = "http://192.168.3.78:50009";
        exportSwagger(api, null);
    }

    /**
     * 导出swagger文档
     *
     * @param api
     * @param response
     * @throws Exception
     */
    public static void exportSwagger(String api, HttpServletResponse response) throws Exception {
        //封装swagger接口数据
        List<ApiTable> list = SwaggerUtils.getSwaggerData(api);
        //按Controller将接口分组
        Map<String, List<ApiTable>> map = list.stream().collect(Collectors.groupingBy(e -> e.getTags(), TreeMap::new, Collectors.toList()));
        //将swagger数据写入Word
        exportSwagger(map, response);
    }

    /**
     * 导出swagger文档
     *
     * @param map
     * @param response
     * @throws Exception
     */
    public static void exportSwagger(Map<String, List<ApiTable>> map, HttpServletResponse response) throws Exception {
        XWPFDocument document = new XWPFDocument();
        IntStream.range(1, 5).forEach(i -> customStyle(document, String.valueOf(i), i));
        String title = "接口文档";
        createTitle(document, title);
        createRun(document, "1", true, 15, "一、接口说明");
        createRun(document, "2").setText("使用HTTP（HTTPS）协议，以JSON格式进行数据交互，字符编码UTF-8。");
        createRun(document, "1", true, 15, "二、接口列表");
        int i = 0;
        Iterator iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            ++i;
            Map.Entry entry = (Map.Entry) iterator.next();
            List<ApiTable> methodList = (List<ApiTable>) entry.getValue();
            createRun(document, "2", true, 15, i + "." + methodList.get(0).getTags().replaceAll("[\\d.]+", ""));
            int j = 0;
            for (ApiTable apiTable : methodList) {
                ++j;
                createRun(document, "3", true, null, i + "." + j + apiTable.getTitle());
                createRun(document, "4").setText(i + "." + j + ".1 接口地址");
                createRun(document).setText(apiTable.getUrl());
                createRun(document, "4").setText(i + "." + j + ".2 请求方法");
                createRun(document).setText(apiTable.getMethod().toUpperCase());
                createRun(document, "4").setText(i + "." + j + ".3 请求参数");
                List<ApiParam> requestList = apiTable.getRequestList();
                if (requestList == null || requestList.isEmpty()) {
                    createRun(document).setText("无");
                } else {
                    XWPFTable requestTable = document.createTable(1, 4);
                    requestTable.getCTTbl().addNewTblPr().addNewTblW().setW(BigInteger.valueOf(8000));
                    requestTable.getRow(0).getTableCells().stream().forEach(o -> {
                        o.setColor("cccccc");
                        o.setWidth("20%");
                    });
                    createRun(requestTable, 0, 0, ParagraphAlignment.LEFT, "参数名");
                    createRun(requestTable, 0, 1, ParagraphAlignment.LEFT, "类型");
                    createRun(requestTable, 0, 2, ParagraphAlignment.LEFT, "是否必填");
                    createRun(requestTable, 0, 3, ParagraphAlignment.LEFT, "描述");
                    for (int k = 0; k < requestList.size(); k++) {
                        ApiParam apiParam = requestList.get(k);
                        requestTable.createRow();
                        createRun(requestTable, k + 1, 0, ParagraphAlignment.LEFT, apiParam.getName());
                        createRun(requestTable, k + 1, 1, ParagraphAlignment.LEFT, apiParam.getType());
                        createRun(requestTable, k + 1, 2, ParagraphAlignment.LEFT, apiParam.getRequired());
                        createRun(requestTable, k + 1, 3, ParagraphAlignment.LEFT, apiParam.getComment());
                    }
                }
                createRun(document, "4").setText(i + "." + j + ".4 响应参数");
                List<ApiParam> responseList = apiTable.getResponseList();
                if (responseList == null || responseList.isEmpty()) {
                    createRun(document).setText("无");
                } else {
                    XWPFTable responseTable = document.createTable(1, 3);
                    responseTable.getCTTbl().addNewTblPr().addNewTblW().setW(BigInteger.valueOf(8000));
                    responseTable.getRow(0).getTableCells().stream().forEach(o -> {
                        o.setColor("cccccc");
                        o.setWidth("20%");
                    });
                    createRun(responseTable, 0, 0, ParagraphAlignment.LEFT, "参数名");
                    createRun(responseTable, 0, 1, ParagraphAlignment.LEFT, "类型");
                    createRun(responseTable, 0, 2, ParagraphAlignment.LEFT, "描述");
                    int[] l = {0};
                    for (ApiParam apiParam : responseList) {
                        ++l[0];
                        responseTable.createRow();
                        createRun(responseTable, l[0], 0, ParagraphAlignment.LEFT, apiParam.getName());
                        createRun(responseTable, l[0], 1, ParagraphAlignment.LEFT, apiParam.getType());
                        createRun(responseTable, l[0], 2, ParagraphAlignment.LEFT, apiParam.getComment());
                    }
                }
            }
        }
        title = URLEncoder.encode(title, "UTF8");
        response.setHeader("Content-disposition", "attachment; filename=" + title + ".docx");
        OutputStream os = response.getOutputStream();
        document.write(os);
        os.close();
    }

    /**
     * 导出数据库表
     *
     * @param dbInfo
     * @param response
     */
    public static void exportDbTable(Map<String, Object> dbInfo, HttpServletResponse response) {
        String ip = dbInfo.get("ip").toString();
        String port = dbInfo.get("port").toString();
        String schema = dbInfo.get("schema").toString();
        String user = dbInfo.get("user").toString();
        String password = dbInfo.get("password").toString();
        List<String> tableNameList = (List<String>) dbInfo.get("tableNameList");
        List<Map<String, String>> list = SqlUtils.selectTableInfo(ip, port, schema, user, password);
        Map<String, List<Map<String, String>>> map = list.stream().collect(Collectors.groupingBy(e -> e.get("tableName"), TreeMap::new, Collectors.toList()));
        String targetUrl = "/data/file/数据库表设计.docx";
        try {
            OutputStream os = response == null ? new FileOutputStream(new File(targetUrl)) : response.getOutputStream();
            XWPFDocument document = new XWPFDocument();
            IntStream.range(1, 2).forEach(i -> customStyle(document, String.valueOf(i), i));
            int i = 0;
            Iterator tableIt = map.entrySet().iterator();
            while (tableIt.hasNext()) {
                Map.Entry entry = (Map.Entry) tableIt.next();
                List<Map<String, String>> columnList = (List<Map<String, String>>) entry.getValue();
                String tableName = columnList.get(0).get("tableName");
                if (!tableNameList.contains(tableName)) {
                    continue;
                }
                ++i;
                XWPFRun run = createRun(document, "1");
                run.setBold(true);
                run.setFontSize(15);
                run.setText(i + "." + tableName + "：" + columnList.get(0).get("tableComment"));
                XWPFTable requestTable = document.createTable(1, 5);
                requestTable.getCTTbl().addNewTblPr().addNewTblW().setW(BigInteger.valueOf(8000));
                requestTable.getRow(0).getTableCells().stream().forEach(o -> {
                    o.setColor("cccccc");
                    o.setWidth("20%");
                });
                createRun(requestTable, 0, 0, ParagraphAlignment.CENTER, "字段名称");
                createRun(requestTable, 0, 1, ParagraphAlignment.CENTER, "描述");
                createRun(requestTable, 0, 2, ParagraphAlignment.CENTER, "类型");
                createRun(requestTable, 0, 3, ParagraphAlignment.CENTER, "是否可为空");
                createRun(requestTable, 0, 4, ParagraphAlignment.CENTER, "是否主键");
                for (int k = 0; k < columnList.size(); k++) {
                    Map<String, String> parameter = columnList.get(k);
                    requestTable.createRow();
                    createRun(requestTable, k + 1, 0, ParagraphAlignment.CENTER, parameter.get("columnName"));
                    createRun(requestTable, k + 1, 1, ParagraphAlignment.CENTER, parameter.get("columnComment"));
                    createRun(requestTable, k + 1, 2, ParagraphAlignment.CENTER, parameter.get("columnType"));
                    createRun(requestTable, k + 1, 3, ParagraphAlignment.CENTER, "NO".equals(parameter.get("isNullable")) ? "否" : "是");
                    createRun(requestTable, k + 1, 4, ParagraphAlignment.CENTER, "PRI".equals(parameter.get("columnKey")) ? "是" : "");
                }
            }
            if (response != null) {
                String title = URLEncoder.encode("数据库表设计", "UTF8");
                response.setHeader("Content-disposition", "attachment; filename=" + title + ".docx");
            }
            document.write(os);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 填充模板并导出
     *
     * @param templatePath
     * @param params
     * @param fileName
     * @param response
     */
    public static void fillExport(String templatePath, Map<String, Object> params, String fileName, HttpServletResponse response) {
        try (
                InputStream is = new ClassPathResource(templatePath).getInputStream();
                OutputStream os = response.getOutputStream()
        ) {
            XWPFDocument doc = new XWPFDocument(is);
            Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
            XWPFParagraph para;
            while (iterator.hasNext()) {
                para = iterator.next();
                List<XWPFRun> runs;
                if (matcher(para.getParagraphText()).find()) {
                    runs = para.getRuns();
                    int start = -1;
                    int end = -1;
                    String str = "";
                    for (int i = 0; i < runs.size(); i++) {
                        XWPFRun run = runs.get(i);
                        String runText = run.toString();
                        if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
                            start = i;
                        }
                        if ((start != -1)) {
                            str += runText;
                        }
                        if ('}' == runText.charAt(runText.length() - 1)) {
                            if (start != -1) {
                                end = i;
                                break;
                            }
                        }
                    }
                    for (int i = start; i <= end; i++) {
                        para.removeRun(i);
                        i--;
                        end--;
                    }
                    for (Map.Entry<String, Object> entry : params.entrySet()) {
                        String key = entry.getKey();
                        if (str.indexOf(key) != -1) {
                            Object value = entry.getValue();
                            if (value instanceof String) {
                                str = str.replace(key, value.toString());
                                para.createRun().setText(str, 0);
                                break;
                            } else if (value instanceof Map) {
                                str = str.replace(key, "");
                                Map pic = (Map) value;
                                int width = Integer.parseInt(String.valueOf(pic.get("width")));
                                int height = Integer.parseInt(String.valueOf(pic.get("height")));
                                byte[] byteArray = (byte[]) pic.get("content");
                                try {
                                    para.createRun().addPicture(new ByteArrayInputStream(byteArray), XWPFDocument.PICTURE_TYPE_PICT, "", Units.toEMU(width), Units.toEMU(height));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static Matcher matcher(String str) {
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 创建标题
     *
     * @param document
     * @param title
     */
    private static void createTitle(XWPFDocument document, String title) {
        XWPFParagraph titleParagraph = document.createParagraph();
        titleParagraph.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun titleRun = titleParagraph.createRun();
        titleRun.setText(title);
        titleRun.setBold(true);
        titleRun.setFontSize(20);
        titleRun.addCarriageReturn();
    }

    /**
     * 创建文本
     *
     * @param document
     * @return
     */
    private static XWPFRun createRun(XWPFDocument document) {
        return createRun(document, null);
    }

    /**
     * 创建文本，设置标题样式
     *
     * @param document
     * @param style
     * @return
     */
    private static XWPFRun createRun(XWPFDocument document, String style) {
        XWPFParagraph paragraph = document.createParagraph();
        if (style != null) {
            paragraph.setStyle(style);
        }
        return paragraph.createRun();
    }

    /**
     * 创建文本，设置标题样式，是否加粗，字体大小，文本内容
     *
     * @param document
     * @param style
     * @param bold
     * @param fontSize
     * @param text
     * @return
     */
    private static XWPFRun createRun(XWPFDocument document, String style, boolean bold, Integer fontSize, String text) {
        XWPFParagraph paragraph = document.createParagraph();
        if (style != null) {
            paragraph.setStyle(style);
        }
        XWPFRun run = paragraph.createRun();
        if (bold) {
            run.setBold(true);
        }
        if (fontSize != null) {
            run.setFontSize(fontSize);
        }
        if (text != null) {
            run.setText(text);
        }
        return run;
    }

    /**
     * 创建文本
     *
     * @param requestTable
     * @param row
     * @param cell
     * @param align
     * @param value
     */
    private static void createRun(XWPFTable requestTable, int row, int cell, ParagraphAlignment align, String value) {
        XWPFParagraph paragraph = requestTable.getRow(row).getCell(cell).getParagraphs().get(0);
        paragraph.setAlignment(align);
        paragraph.createRun().setText(value);
    }

    /**
     * 自定义标题样式
     *
     * @param docxDocument
     * @param styleId
     * @param headingLevel
     */
    private static void customStyle(XWPFDocument docxDocument, String styleId, int headingLevel) {
        CTStyle style = CTStyle.Factory.newInstance();
        style.setStyleId(styleId);
        CTDecimalNumber decimalNumber = CTDecimalNumber.Factory.newInstance();
        decimalNumber.setVal(BigInteger.valueOf(headingLevel));
        style.setUiPriority(decimalNumber);
        CTOnOff onOff = CTOnOff.Factory.newInstance();
        style.setUnhideWhenUsed(onOff);
        style.setQFormat(onOff);
        CTPPr ppr = CTPPr.Factory.newInstance();
        ppr.setOutlineLvl(decimalNumber);

        CTSpacing spacing = ppr.getSpacing() != null ? ppr.getSpacing() : ppr.addNewSpacing();
        //设置1.5倍行间距
        spacing.setLine(new BigInteger("360"));

        style.setPPr(ppr);
        XWPFStyle xwpfStyle = new XWPFStyle(style);
        xwpfStyle.setType(STStyleType.PARAGRAPH);
        XWPFStyles xwpfStyles = docxDocument.createStyles();
        xwpfStyles.addStyle(xwpfStyle);
    }
}